package com.credithc.eagleeye.plugins.gateway;
import com.credithc.eagleeye.agent.factory.EagleEyeAgentFactory;
import com.credithc.eagleeye.agent.util.HostUtil;
import com.credithc.eagleeye.constants.EagleEyeConstant;
import com.credithc.eagleeye.constants.RpcTypeEnum;
import com.credithc.eagleeye.thread.EagleEyeThreadContext;
import com.credithc.eagleeye.thread.TraceInfo;
import com.credithc.eagleeye.util.MixUtil;
import com.credithc.eagleeye.vo.RpcTraceInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.Date;
/**
 * 描 述: <br/>
 * 作 者: zhangjin<br/>
 * 创 建：2019年11月08日<br/>
 * 版 本：v1.0.0<br>
 * <p>
 * 历 史: (版本) 作者 时间 注释 <br/>
 */
@SuppressWarnings("DuplicatedCode")
public class EagleEyeGatewayFilterNoBody implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        TraceInfo traceInfo = initEagleEyeTraceInfo(request);
        RpcTraceInfoVo rpcTraceInfoVo = initRpcTraceInfoVo(traceInfo, exchange);
        //鹰眼header传播
        ServerHttpRequestDecorator newRequest = new ServerHttpRequestDecorator(request) {
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(request.getHeaders());
                httpHeaders.set(EagleEyeConstant.HC_TRACE_ID, traceInfo.getTraceId());
                httpHeaders.set(EagleEyeConstant.HC_RPC_ID, traceInfo.getRpcId());
                return httpHeaders;
            }
        };
        return chain.filter(exchange.mutate().request(newRequest).build()).then(Mono.fromRunnable(()-> {
            send2EagleEye(rpcTraceInfoVo,exchange.getResponse());
        }));

    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    private TraceInfo initEagleEyeTraceInfo(ServerHttpRequest request) {
        EagleEyeThreadContext.init();
        TraceInfo traceInfo = new TraceInfo();
        String hcTraceId = request.getHeaders().getFirst(EagleEyeConstant.HC_TRACE_ID);
        if (StringUtils.isNotBlank(hcTraceId)) {
            traceInfo.setTraceId(hcTraceId);
        }

        String hcRpcId = request.getHeaders().getFirst(EagleEyeConstant.HC_RPC_ID);
        if (hcRpcId != null && hcRpcId.length() > 0) {
            traceInfo.setRpcId(hcRpcId);
            traceInfo.addHierarchy();
            traceInfo.addSequenceNo();
        }

        EagleEyeThreadContext.putTraceInfo(traceInfo);
        MDC.put("traceId", traceInfo.getTraceId());
        MDC.put("rpcId", traceInfo.getRpcId());
        MDC.put("clientIp", getClientIp(request));
        return traceInfo;
    }

    private RpcTraceInfoVo initRpcTraceInfoVo(TraceInfo traceInfo, ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        RpcTraceInfoVo rpcTraceInfoVo = new RpcTraceInfoVo();
        Date date = new Date();
        rpcTraceInfoVo.setRequestDateTime(MixUtil.ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS.format(date));
        rpcTraceInfoVo.setTraceId(traceInfo.getTraceId());
        rpcTraceInfoVo.setRpcId(traceInfo.getRpcId());
        rpcTraceInfoVo.setRpcType(RpcTypeEnum.HTTP.getRpcName());
        rpcTraceInfoVo.setServiceCategory("SpringGateway");
        rpcTraceInfoVo.setServiceName("SpringGateway");
        String path = StringUtils.removeStart(request.getPath().pathWithinApplication().value(),"/");
        rpcTraceInfoVo.setMethodName(path);
        rpcTraceInfoVo.setClientHost(HostUtil.getIp());
        rpcTraceInfoVo.setServerHost(request.getURI().toString());
        rpcTraceInfoVo.setRequestSize(0);
        rpcTraceInfoVo.setRunTime(System.currentTimeMillis());
        // 获取路由的目标URI
        URI targetUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        HttpMethod method = request.getMethod();
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("targetUri:%s\n" ,targetUri==null?path:targetUri.toString()));
        sb.append(String.format("method: %s\n" ,method==null?"":method.name()));
        sb.append(String.format("headers size: %s\n",request.getHeaders().size()));
        sb.append("-------------\n");
        request.getHeaders().forEach((key,value)->{
            sb.append(String.format("%s: %s\n", key, String.join(",", value)));
        });
        rpcTraceInfoVo.setRequestJson(sb.toString());
        return rpcTraceInfoVo;
    }

    private void send2EagleEye(RpcTraceInfoVo rpcTraceInfoVo,ServerHttpResponse response){
        try {
            rpcTraceInfoVo.setRunTime(System.currentTimeMillis()-rpcTraceInfoVo.getRunTime());
            HttpStatus statusCode = response.getStatusCode();
            rpcTraceInfoVo.setResponseJson(statusCode.toString());
            if(statusCode.isError()){
                rpcTraceInfoVo.setResult("ERROR");
            }else{
                rpcTraceInfoVo.setResult("OK");
            }
            EagleEyeAgentFactory.getInstance().send2EagleEye(rpcTraceInfoVo);
        } catch (Exception ex) {
        }
    }


    private String getClientIp(ServerHttpRequest request) {
        if (request == null) {
            return "";
        }
        String ip = request.getHeaders().getFirst("x-forwarded-for");
        if (ip == null || ip.length() == 0) {
            ip = request.getHeaders().getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0) {
            ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0) {
            ip = request.getHeaders().getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        if (ip != null) {
            int index = ip.indexOf(",");
            ip=(index > -1)? ip.substring(0, index) : ip;
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }


}
